/*
 * NaluAnalyzer.cpp
 *
 *  Created: 2012-05-10
 *   Author: terry
 */

#include "NaluAnalyzer.h"
#include <assert.h>

NaluAnalyzer::NaluAnalyzer():
    m_pSink(),
    m_lastType()
{
    m_buffer.ensure(1024 * 500);
    m_compoundBuffer.ensure(1024 * 700);
}

NaluAnalyzer::~NaluAnalyzer()
{

}

bool NaluAnalyzer::inputData(const uint8_t* buffer, size_t length)
{
    if (length == 0)
    {
        return false;
    }

    m_buffer.write(buffer, length);
    
    bool found = false;
    while (true)
    {
        uint8_t* data = m_buffer.getReadPtr();
        size_t size = m_buffer.readable();

        NaluPacket firstPacket;
        if (!findNalu(data, size, 0, firstPacket))
        {
            break;
        }

        NaluPacket secondPacket;
        if (!findNalu(data, size, firstPacket.length + firstPacket.prefix, secondPacket))
        {
            break;
        }

        firstPacket.length = secondPacket.length - firstPacket.length;
        onNewPacket(firstPacket);
        m_buffer.skip(secondPacket.length);
        found = true;
    }

    return found;
}

void NaluAnalyzer::setSink(NaluAnalyzerSink* pSink)
{
    m_pSink = pSink;
}

void NaluAnalyzer::clear()
{
    m_buffer.clear();
}

void NaluAnalyzer::onNewPacket(NaluPacket& packet)
{
    if (packet.type == NaluPacket::NALU_SPS)
    {
        if (m_compoundBuffer.readable() > 0)
        {
            flushCompoundBuffer();
        }
    }

    if ((packet.type == NaluPacket::NALU_SPS) ||
        (packet.type == NaluPacket::NALU_PPS) ||
        (packet.type == NaluPacket::NALU_SEI) ||
        (packet.type == NaluPacket::NALU_IFRAME)
        )
    {
        m_compoundBuffer.write(packet.data, packet.length);
        m_lastType = packet.type;
        return;
    }
    
    if (packet.type == NaluPacket::NALU_NULL)
    {
        if (packet.length > 28)
        {
            if (packet.data[16] == 0x11)
            {
                if (!m_compoundBuffer.empty())
                {
                    m_compoundBuffer.write(packet.data + 18, packet.length - 18);
                }
            }
        }

        return;
    }
    
    flushCompoundBuffer();

    writePacket(packet);
}

void NaluAnalyzer::writePacket(NaluPacket& packet)
{
    if (!m_pSink)
    {
        return;
    }

    m_pSink->writePacket(packet);
}

void NaluAnalyzer::flushCompoundBuffer()
{
    if (!m_compoundBuffer.empty())
    {
        NaluPacket lastPkt;
        lastPkt.data = m_compoundBuffer.getReadPtr();
        lastPkt.length = m_compoundBuffer.readable();
        lastPkt.type = m_lastType;
        lastPkt.prefix = 4;

        writePacket(lastPkt);

        m_compoundBuffer.clear();
    }
}

bool NaluAnalyzer::findNalu(uint8_t* buffer, size_t length, size_t start, NaluPacket& packet)
{
    if ((length < 3) || ((length - start) < 3))
    {
        return false;
    }

    bool found = false;
    uint8_t* p = buffer;
    for (size_t i = start; i < (length - 3); ++ i)
    {
        if ((p[i] == 0) && (p[i+1] == 0))
        {
            if (p[i+2] == 0)
            {
                if (((i + 3) < length) && (p[i+3] == 1))
                {
                    packet.data = p + i;
                    packet.length = i;
                    packet.prefix = 4;
                    packet.type = packet.data[4] & 0x1F;

                    found = true;
                    break;
                }
            }
            else if (p[i+2] == 1)
            {
                packet.data = p + i;
                packet.length = i;
                packet.prefix = 3;
                packet.type = packet.data[3] & 0x1F;

                found = true;
                break;
            }
        }
    }
    return found;
}

